//
//  GBFileManager.m
//  PngOK
//
//  Created by wanglidong on 14-5-5.
//  Copyright (c) 2014年 GoldenBull. All rights reserved.
//

#import "GBFileManager.h"
#import <dirent.h>
#import <sys/stat.h>

NSString* stringWithSizeX(double size)
{
    if(size < GB_STORAGE_ARY)
    {
        return [NSString stringWithFormat:SizeFormat_KB,1.0f];
    }
    
    NSString* strFileInfoFormat = nil;
    size /= GB_STORAGE_ARY;
    if (size < GB_STORAGE_ARY)
    {
        strFileInfoFormat = SizeFormat_KB;
    }
    else
    {
        size /= GB_STORAGE_ARY;
        if (size < GB_STORAGE_ARY)
            strFileInfoFormat = SizeFormat_MB;
        else
        {
            size /= GB_STORAGE_ARY;
            strFileInfoFormat = SizeFormat_GB;
        }
    }
    
    return [NSString stringWithFormat:strFileInfoFormat,size];
}

NSString* stringWithSizeAllowB(double size)
{
    if(size <= 0.0f)
    {
        return [NSString stringWithFormat:SizeFormat_KB,0.0f];
    }
    else if(size < GB_STORAGE_ARY)
    {
        return [NSString stringWithFormat:SizeFormat_B,size];
    }
    
    return stringWithSizeX(size);
}

NSString* stringWithSizeAvoidZero(double size)
{
    if(size <= 0.0f)
    {
        return @"--";
    }
    
    return stringWithSizeAllowB(size);
}

#pragma mark - 目录结构
void addFileToList(NSString *rootPath, NSString *path, NSString *name, off_t size, NSMutableArray *files, double *totalSize)
{
    
    NSString *relativePath = [path stringByReplacingOccurrencesOfString:rootPath withString:@""];
    
    //NSLog(@"%@,%@,%@,%lld,%@",rootPath,path,name,size,relativePath);
    (*totalSize) += size;
    NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                 [path stringByAppendingPathComponent:name],@"fullPath",
                                 relativePath.length == 0 ? @"/" : relativePath,@"relativePath",
                                 name,@"name",
                                 [NSNumber numberWithLong:size],@"size",
                                 [NSNumber numberWithUnsignedInteger:[files count]+1],@"seq",
                                 nil];
    [files addObject:dict];
}
// 遍历寻找文件
void filesHasExtension(NSString *rootPath, const char *folderPath, NSString *ext, NSMutableArray *files, double *totalSize)
{
    //printf("serach path : %s\n", folderPath);
    DIR* dir = opendir(folderPath);
    if (dir == NULL) return;
    struct dirent* child;
    while ((child = readdir(dir))!=NULL) {
        if (child->d_type == DT_DIR && (
                                        (child->d_name[0] == '.' && child->d_name[1] == 0) || // 忽略目录 .
                                        (child->d_name[0] == '.' && child->d_name[1] == '.' && child->d_name[2] == 0) // 忽略目录 ..
                                        )) continue;
        
        size_t folderPathLength = strlen(folderPath);
        char childPath[1024]; // 子文件的路径地址
        stpcpy(childPath, folderPath);
        if (folderPath[folderPathLength-1] != '/'){
            childPath[folderPathLength] = '/';
            folderPathLength++;
        }
        stpcpy(childPath+folderPathLength, child->d_name);
        childPath[folderPathLength + child->d_namlen] = 0;
        
        if (child->d_type == DT_DIR){ // directory
            
            filesHasExtension(rootPath,childPath, ext, files, totalSize); // 递归调用子目录
            
        }else if (child->d_type == DT_REG)
        { // file or link
            NSString *name = [NSString stringWithUTF8String:child->d_name];
            
            if ([[name pathExtension] isEqualToString:ext])
            {
                struct stat st;
                off_t size = 0;
                if(lstat(childPath, &st) == 0)
                {
                    size = st.st_size;
                }
                
                NSString *path = [NSString stringWithUTF8String:folderPath];
                //                NSString *relativePath = [path stringByReplacingOccurrencesOfString:rootPath withString:@""];
                //                NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                //                                      [path stringByAppendingPathComponent:name],@"fullPath",
                //                                      relativePath.length == 0 ? @"/" : relativePath,@"relativePath",
                //                                      name,@"name",
                //                                    [NSNumber numberWithLong:size],@"size",
                //                                    [NSNumber numberWithUnsignedInteger:[files count]+1],@"seq",
                //                                      nil];
                //                [files addObject:dict];
                
                addFileToList(rootPath, path, name, size, files, totalSize);
            }
            else
            {
                //NSLog(@"%@",name);
            }
        }
    }
    
    // 一定要关闭
    closedir(dir);
}
// 计算目录总大小
off_t folderSizeAtPath(const char *folderPath)
{
    off_t folderSize = 0;
    DIR* dir = opendir(folderPath);
    if (dir == NULL) return 0;
    struct dirent* child;
    while ((child = readdir(dir))!=NULL) {
        if (child->d_type == DT_DIR && (
                                        (child->d_name[0] == '.' && child->d_name[1] == 0) || // 忽略目录 .
                                        (child->d_name[0] == '.' && child->d_name[1] == '.' && child->d_name[2] == 0) // 忽略目录 ..
                                        )) continue;
        
        size_t folderPathLength = strlen(folderPath);
        char childPath[1024]; // 子文件的路径地址
        stpcpy(childPath, folderPath);
        if (folderPath[folderPathLength-1] != '/'){
            childPath[folderPathLength] = '/';
            folderPathLength++;
        }
        stpcpy(childPath+folderPathLength, child->d_name);
        childPath[folderPathLength + child->d_namlen] = 0;
        if (child->d_type == DT_DIR){ // directory
            folderSize += folderSizeAtPath(childPath); // 递归调用子目录
            // 把目录本身所占的空间也加上
            struct stat st;
            if(lstat(childPath, &st) == 0) folderSize += st.st_size;
        }else if (child->d_type == DT_REG || child->d_type == DT_LNK){ // file or link
            struct stat st;
            if(lstat(childPath, &st) == 0)
            {
                folderSize += st.st_size;
            }
        }
    }
    
    // 一定要关闭
    closedir(dir);
    return folderSize;
}

@implementation GBFileManager
#pragma mark - files

+ (unsigned long long)fileSizeOfItemAtPath:(NSString *)path
{
    return [[[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil] fileSize];
}

+ (GBFileType)typeOfItemAtPath:(NSString *)path
{
    BOOL isDir = NO;
    if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
        
        return isDir ? GBFileTypeFolder : GBFileTypeFile;
    }
    return GBFileTypeNone;
}

+ (BOOL)isExist:(NSString *)path isDirectory:(BOOL)isDirectory
{
    NSFileManager* fm = [NSFileManager defaultManager];
    BOOL bIsDir = NO;
	return ([fm fileExistsAtPath:path isDirectory:&bIsDir] && (bIsDir == isDirectory));
}

// 新建一个目录或文件
+ (BOOL)newItem:(NSString *)path error:(NSError **)error isDirectory:(BOOL)isDirectory force:(BOOL)shouldForce NS_AVAILABLE(10_5, 2_0)
{
    
    if(path == nil) return NO;
    
    NSFileManager *fm = [NSFileManager defaultManager];
    
    if (shouldForce) {
        // 强制新建, 先删除旧的, 再新建
        if ([fm fileExistsAtPath:path isDirectory:NULL]) {
            [fm removeItemAtPath:path error:nil];
        }
    }
    else {
        BOOL bIsDir = isDirectory;
        if ([fm fileExistsAtPath:path isDirectory:&bIsDir] && (bIsDir == isDirectory)) {
            
            // 存在
            return YES;
            
        } else if(bIsDir != isDirectory) {
            // 存在但类型不对
            [fm removeItemAtPath:path error:nil];
        }
    }
    
    // 创建
    if (isDirectory) {
        return [fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
    }
    else {
        
        // 上级目录必须要存在
        [self newItem:[path stringByDeletingLastPathComponent] error:error isDirectory:YES force:NO];
        
        return [fm createFileAtPath:path contents:nil attributes:nil];
    }
}

+ (BOOL)newFolder:(NSString *)path error:(NSError **)error force:(BOOL)shouldForce NS_AVAILABLE(10_5, 2_0)
{
    return [self newItem:path error:error isDirectory:YES force:shouldForce];
}

+ (BOOL)newFolder:(NSString *)path force:(BOOL)shouldForce NS_AVAILABLE(10_5, 2_0)
{
    return [self newItem:path error:nil isDirectory:YES force:shouldForce];
}

+ (BOOL)newFile:(NSString *)path force:(BOOL)shouldForce NS_AVAILABLE(10_5, 2_0)
{
    return [self newItem:path error:nil isDirectory:NO force:shouldForce];
}

+ (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath force:(BOOL)shouldForce NS_AVAILABLE(10_5, 2_0)
{
    NSFileManager *fm = [NSFileManager defaultManager];
    
    // 强制拷贝, 已存在, 删除, 拷贝 --- 既然存在, 就无需检查其上级目录了
    if (shouldForce && [fm fileExistsAtPath:dstPath isDirectory:NULL]) {
        [fm removeItemAtPath:dstPath error:nil];
        return [[NSFileManager defaultManager] copyItemAtPath:srcPath toPath:dstPath error:nil];
    }
    else if(![fm fileExistsAtPath:dstPath isDirectory:NULL])
    {
        // 目标已存在时操作会失败 --- 上级目录必须存在
        if([self newFolder:[dstPath stringByDeletingLastPathComponent] error:nil force:NO])
        {
            return [fm copyItemAtPath:srcPath toPath:dstPath error:nil];
        }
    }
    
    return NO;
}
+ (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error NS_AVAILABLE(10_5, 2_0)
{
    return [[NSFileManager defaultManager] copyItemAtPath:srcPath toPath:dstPath error:error];
}

+ (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath force:(BOOL)shouldForce NS_AVAILABLE(10_5, 2_0)
{
    NSFileManager *fm = [NSFileManager defaultManager];
    
    // 强制拷贝, 已存在, 删除, 拷贝 --- 既然存在, 就无需检查其上级目录了
    if (shouldForce && [fm fileExistsAtPath:dstPath isDirectory:NULL]) {
        [fm removeItemAtPath:dstPath error:nil];
        return [[NSFileManager defaultManager] moveItemAtPath:srcPath toPath:dstPath error:nil];
    }
    else if(![fm fileExistsAtPath:dstPath isDirectory:NULL])
    {
        // 目标已存在时操作会失败 --- 上级目录必须存在
        if([self newFolder:[dstPath stringByDeletingLastPathComponent] error:nil force:NO])
        {
            return [fm moveItemAtPath:srcPath toPath:dstPath error:nil];
        }
    }
    
    return NO;
}
+ (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error NS_AVAILABLE(10_5, 2_0)
{
    return [[NSFileManager defaultManager] moveItemAtPath:srcPath toPath:dstPath error:error];
}

+ (BOOL)removeItemAtPath:(NSString *)path NS_AVAILABLE(10_5, 2_0)
{
    NSFileManager* fm = [NSFileManager defaultManager];
    
    return [fm fileExistsAtPath:path] && [fm removeItemAtPath:path error:nil];
}

+ (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error NS_AVAILABLE(10_5, 2_0)
{
    return [[NSFileManager defaultManager] removeItemAtPath:path error:error];
}

#if GBFileManager_Allload

// 取得系统属性
+ (id)deviceAttributesForKey:(id)key
{
    NSDictionary *fattributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil];
    return [fattributes objectForKey:key];
}

// 设置文件（夹）属性
+ (BOOL)setAttribute:(id)value forKey:(id)key ofItemAtPath:(NSString *)path error:(NSError **)error NS_AVAILABLE(10_5, 2_0)
{
    if(value && key)
    {
        NSFileManager *fm = [NSFileManager defaultManager];
        NSDictionary *attr = [NSDictionary dictionaryWithObject:value  forKey:key];
        return [fm setAttributes:attr ofItemAtPath:path error:error];
    }
    
    return NO;
}

+ (BOOL)setModifyTime:(NSDate *)date ofItemAtPath:(NSString *)path NS_AVAILABLE(10_5, 2_0)
{
    return [self setAttribute:date forKey:NSFileModificationDate ofItemAtPath:path error:nil];
}
+ (BOOL)setModifyTime:(NSDate *)date ofItemAtPath:(NSString *)path  error:(NSError **)error NS_AVAILABLE(10_5, 2_0)
{
    return [self setAttribute:date forKey:NSFileModificationDate ofItemAtPath:path error:error];
}

+ (long long)sizeOfFolder:(NSString *)path
{
    return folderSizeAtPath([path cStringUsingEncoding:NSUTF8StringEncoding]);
}

+ (long long)deviceTotalSize
{
    return [[self deviceAttributesForKey:NSFileSystemFreeSize] longLongValue];
}

+ (long long)deviceFreeSize
{
    return [[self deviceAttributesForKey:NSFileSystemFreeSize] longLongValue];
}

#pragma mark - path

+ (BOOL)removeFileAtPath:(NSString *)path error:(NSError **)error NS_AVAILABLE(10_5, 2_0)
{
    NSFileManager* fm = [NSFileManager defaultManager];
    
    BOOL bIsDir = NO;
    if (([fm fileExistsAtPath:path isDirectory:&bIsDir] && !bIsDir)) {
        return [fm removeItemAtPath:path error:error];
    }
    
    return NO;
}

+ (BOOL)removeFileAtPath:(NSString *)path NS_AVAILABLE(10_5, 2_0)
{
    return [self removeFileAtPath:path error:nil];
}


+ (BOOL)removeFolderAtPath:(NSString *)path error:(NSError **)error NS_AVAILABLE(10_5, 2_0)
{
    NSFileManager* fm = [NSFileManager defaultManager];
    
    BOOL bIsDir = NO;
    if (([fm fileExistsAtPath:path isDirectory:&bIsDir] && bIsDir)) {
        return [fm removeItemAtPath:path error:error];
    }
    
    return NO;
}

+ (BOOL)removeFolderAtPath:(NSString *)path NS_AVAILABLE(10_5, 2_0)
{
    return [self removeFolderAtPath:path error:nil];
}


+ (NSOutputStream *)outputStreamToFileAtPath:(NSString *)path append:(BOOL)shouldAppend
{
    if(path == nil) return nil;
    
    NSFileManager *fm = [NSFileManager defaultManager];
    if(shouldAppend)
    {
        // is here ?
        BOOL bIsDir = NO;
        if([fm fileExistsAtPath:path isDirectory:&bIsDir] && !bIsDir)
        {
            return [NSOutputStream outputStreamToFileAtPath:path append:YES];
        }
    }
    
    // 不追加，原文件删除
    [self newFile:path force:YES];
	return [NSOutputStream outputStreamToFileAtPath:path append:NO];
}

+ (NSArray *)childrenAtPath:(NSString *)path
{
    if(path == nil) return nil;
    const char *folderPath = [path cStringUsingEncoding:NSUTF8StringEncoding];
    NSMutableArray *files = [NSMutableArray array];
    DIR* dir = opendir(folderPath);
    if (dir == NULL) return nil;
    struct dirent* child;
    while ((child = readdir(dir))!=NULL) {
        
        if(
           (child->d_name[0] == '.' && child->d_name[1] == 0) || // 忽略目录 .
           (child->d_name[0] == '.' && child->d_name[1] == '.' && child->d_name[2] == 0) // 忽略目录 ..
           ) continue;
        
        [files addObject:[NSString stringWithUTF8String:child->d_name]];
    }
    
    closedir(dir);
    return files;
}

+ (NSArray *)filesAtPath:(NSString *)path
{
    if(path == nil) return nil;
    const char *folderPath = [path cStringUsingEncoding:NSUTF8StringEncoding];
    NSMutableArray *files = [NSMutableArray array];
    DIR* dir = opendir(folderPath);
    if (dir == NULL) return nil;
    struct dirent* child;
    while ((child = readdir(dir))!=NULL) {
        if (child->d_type == DT_DIR && (
                                        (child->d_name[0] == '.' && child->d_name[1] == 0) || // 忽略目录 .
                                        (child->d_name[0] == '.' && child->d_name[1] == '.' && child->d_name[2] == 0) // 忽略目录 ..
                                        )) continue;
        
        //子文件名
        if (child->d_type == DT_REG)
        {
            [files addObject:[NSString stringWithUTF8String:child->d_name]];
        }
    }
    
    closedir(dir);
    return files;
}

#endif
@end
